﻿namespace PugLib
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq.Expressions;
    using System.Reflection;

    public static class Reflect
    {
        public static object Get(Type t, string fieldName, object value)
        {
            if (null == value)
            {
                return null;
            }

            PropertyInfo propertyInfo = t.GetProperty(fieldName);

            return propertyInfo == null ? null : propertyInfo.GetValue(value, null);
        }

        public static object Get<T>(string fieldName, T value)
        {
            if (null == value)
            {
                return null;
            }

            PropertyInfo propertyInfo = value.GetType().GetProperty(fieldName);

            return propertyInfo == null ? null : propertyInfo.GetValue(value, null);
        }

        public static string GetString<T>(string fieldName, T value)
        {
            if (null == value)
            {
                return null;
            }

            PropertyInfo propertyInfo = value.GetType().GetProperty(fieldName);

            return propertyInfo == null ? string.Empty : SafeConvert.ToString(propertyInfo.GetValue(value, null));
        }

        /// <summary>
        /// Gets fieldName property value from obj T and returns it as type Tout
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="fieldName"></param>        
        /// <param name="value"></param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Generic return type")]
        public static TOut GetAndConvert<T, TOut>(string fieldName, T value)
        {
            if (null == value)
            {
                return default(TOut);
            }

            PropertyInfo propertyInfo = value.GetType().GetProperty(fieldName);

            return propertyInfo == null ? default(TOut) : SafeConvert.GetAs<TOut>(propertyInfo.GetValue(value, null));
        }


        public static object Get(this object value, Type type, string fieldName)
        {
            return Get(type, fieldName, value);
        }


        /// <summary>
        /// Gets the MethodInfo for the method that is called in the provided <paramref name="expression"/>
        /// </summary>
        /// <typeparam name="TClass">The type of the class.</typeparam>
        /// <param name="expression">The expression.</param>
        /// <returns>Method info</returns>
        /// <exception cref="ArgumentException">The provided expression is not a method call</exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "caller wants a type")]
        public static MethodInfo GetMethod<TClass>(Expression<Action<TClass>> expression)
        {
            MethodCallExpression methodCall = expression.Body as MethodCallExpression;
            if (methodCall == null)
            {
                throw new ArgumentException("Expected method call");
            }
            return methodCall.Method;
        }

        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
        public static PropertyInfo GetProperty<TClass, TValue>(Expression<Func<TClass, TValue>> expression)
        {
            MemberExpression memberExpression = expression.Body as MemberExpression;
            if (memberExpression == null)
            {
                throw new ArgumentException("Expected property expression");
            }
            PropertyInfo info = memberExpression.Member as PropertyInfo;
            if (info == null)
            {
                throw new ArgumentException("Expected property expression");
            }
            return info;
        }

        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
        public static PropertyInfo GetProperty<TClass>(Expression<Func<TClass, object>> expression)
        {
            MemberExpression memberExpression;
            // if the return value had to be cast to object, the body will be an UnaryExpression
            UnaryExpression unary = expression.Body as UnaryExpression;
            if (unary != null)
            {
                // the operand is the "real" property access
                memberExpression = unary.Operand as MemberExpression;
            }
            else
            {
                // in case the property is of type object the body itself is the correct expression
                memberExpression = expression.Body as MemberExpression;
            }
            // as before:
            if (memberExpression == null)
            {
                throw new ArgumentException("Expected property expression");
            }
            PropertyInfo info = memberExpression.Member as PropertyInfo;
            if (info == null)
            {
                throw new ArgumentException("Expected property expression");
            }
            return info;
        }
    }
}